package org.springblade.modules.ad.service.impl;


import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.modules.ad.entity.AdGroup;
import org.springblade.modules.ad.entity.AdUser;
import org.springblade.modules.ad.service.AdGroupService;
import org.springblade.modules.ad.service.AdUserService;
import org.springblade.modules.ad.utils.LdapUtil;
import org.springblade.modules.ad.utils.Result;
import org.springframework.ldap.InvalidAttributeValueException;
import org.springframework.ldap.NameAlreadyBoundException;
import org.springframework.ldap.NameNotFoundException;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.naming.Name;
import javax.naming.directory.Attribute;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

import static org.springblade.modules.ad.utils.LdapUtil.buildDn;

@Service
public class AdUserServiceImpl implements AdUserService {

	private final Logger logger = LoggerFactory.getLogger(getClass());


	@Resource
	private LdapTemplate ldapTemplate;
	@Resource
	private ThreadPoolTaskExecutor executor;
	@Resource
	private AdGroupService adGroupService;
	/**
	 * 创建用户信息
	 *
	 * @param adUser
	 * @return
	 */
	@Override
	public boolean addAdUser(AdUser adUser) {
		// ldapTemplate.create(adUser);

		Name dn = buildDn(adUser.getBasePath(), adUser.getGivenName());
		DirContextAdapter context = new DirContextAdapter(dn);
		context.setAttributeValues("objectClass", new String[] {"top", "person","organizationalPerson", "user"});


		// 遍历所有属性名和属性值
		logger.debug("AdUser【"+adUser.getDisplayName()+"】属性:");
		if (null != adUser.getUserPassword()) {
			adUser.setUnicodePwd(adUser.setBytePwd("\"" +adUser.getUserPassword()+ "\"") );
		}
		adUser.setUserAccountControl("512");
		objectToContextAttribute(adUser, context);


		try {
			ldapTemplate.bind(context);
		} catch (Exception e) {
			if (e instanceof NameAlreadyBoundException){
				logger.info("该目录下，该用户已存在,异常信息:"+ e.getMessage());
				return false;
			}
			if (e instanceof NameNotFoundException){
				logger.info("请确认改ou目录已存在的情况下创建用户,异常信息:" + e.getMessage());
				return false;
			}
			if (e instanceof InvalidAttributeValueException){
				logger.info("用户属性值异常,异常信息:" + e.getMessage());
				return false;
			}
			e.printStackTrace();
		}
		return true;
	}

	@Override
	public boolean addAdUserAndJoinGroup(AdUser adUser) {
		// 新增用户
		addAdUser(adUser);
		// 用户添加到域组
		userJoinGroupMethod(adUser);
		return false;
	}

	private void userJoinGroupMethod(AdUser adUser) {
		// 一起添加到域组
		AdGroup adGroup = new AdGroup();
		adGroup.setBasePath(adUser.getBasePath());
		String adGroupName = AdGroup.getAdGroupName(Long.valueOf(adUser.getDepartment()));
		adGroup.setName(adGroupName);
		adGroup.setMembers(Arrays.asList(adUser.getGivenName()));
		adGroupService.addUserToGroup(adGroup);
	}


	@Override
	public Result addAdUser(List<AdUser> list) {
		List<String> errObj = Lists.newLinkedList();
		for (AdUser adUser:list){
			executor.execute(() -> {
				if (!addAdUser(adUser)) {
					errObj.add(adUser.getGivenName());
				}
			});
		}
		if (CollectionUtil.isNotEmpty(errObj)) {
			return Result.of().error(errObj);
		}else{
			return Result.of().success();
		}
	}

	/**
	 * 对象属性，属性值 add 到 context
	 * @param adUser
	 * @param context
	 */
	private void objectToContextAttribute(AdUser adUser, DirContextOperations context) {
		Field[] fields = adUser.getClass().getDeclaredFields();
		for (int i=0;i<fields.length;i++){
			Field field = fields[i];
			try {
				field.setAccessible(true);
				logger.debug("属性名:" + field.getName() + " 属性值:" + field.get(adUser));
				if (StringUtil.containsAny("basePath",field.getName()) || StringUtil.isEmpty(field.get(adUser))){
					continue;
				}
				context.setAttributeValue(field.getName(),field.get(adUser));
			} catch (IllegalAccessException e) {
				logger.error("对象值转换异常，异常信息"+e.getMessage());
			}
		}
	}


	@Override
	public void changeUserOu(AdUser ldapUser){
		// 解绑
		ldapTemplate.unbind(buildDn(ldapUser.getOldBasePath(),ldapUser.getGivenName()));
		// 重新绑定
		addAdUser(ldapUser);
	}



	@Override
	public AdUser findByCn(AdUser adUser) {
		try {
			AdUser person =  (AdUser) ldapTemplate.lookup(
				buildDn(adUser.getBasePath(),adUser.getGivenName()), (AttributesMapper) attributes -> LdapUtil.objectToContextAttribute(attributes, new AdUser()));
			System.out.println(person.toString());
			return person;
		} catch (Exception e) {
			if (e instanceof NameNotFoundException){
				logger.info("请确认该ou目录正确，用户信息存在,异常信息:" + e.getMessage());
				return null;
			}
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public void modifyPerson(AdUser ldapUser) {
		Name dn = buildDn(ldapUser.getBasePath(),ldapUser.getGivenName());
		DirContextOperations context = ldapTemplate.lookupContext(dn);
//		if (null != ldapUser.getUserPassword()) {
//			ldapUser.setUnicodePwd(ldapUser.setBytePwd(ldapUser.getUserPassword()));
//		}
		objectToContextAttribute(ldapUser, context);
		ldapTemplate.modifyAttributes(context);
	}

	@Override
	public void modifyUserPwd(AdUser adUser) {
		Name dn = buildDn(adUser.getBasePath(),adUser.getGivenName());
		Attribute attr = new BasicAttribute("unicodePwd", adUser.setBytePwd(adUser.getUserPassword()));
		ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
		ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item});
	}


	@Override
	public void deletePerson(AdUser ldapUser) {
		Name dn = buildDn(ldapUser.getBasePath(),ldapUser.getGivenName());
		Attribute attr = new BasicAttribute("userAccountControl", "514"); // 514禁用  512启用 (暂时发现544也可以启用)
		ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
		ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item});
	}

}
